<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>[2.13]-接口调试：在线SQL语句查看与性能优化 | PhalApi(π框架) - PHP轻量级开源接口框架 - 助你创造价值！</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <meta name="Author" content="PhalApi,ecitlm,dogstar">
    <meta name="description" content="[2.13]-接口调试：在线SQL语句查看与性能优化，PhalApi是一个PHP轻量级开源接口框架，致力于快速开发接口服务。支持HTTP/SOAP/RPC等协议，可用于搭建接口/微服务/RESTful接口/Web Services。我们不断更新，保持生气；为接口负责，为开源负责！并承诺永久免费！">
    <meta name="keywords" content="PhalApi,phalapi,phalapi接口开发,后台接口开发,后台接口开发框架,接口开发,接口框架,开源接口框架,PHP后台接口开发,PHP接口开发,PHP接口框架,PHP后台接口框架,phalapi官网,PHP接口框架,php接口开发框架,php接口开发,php web框架,π框架,π开发框架,π接口框架,π接口开发框架,api接口,php接口,,PHP接口框架,phalapi文档,phalapi wiki,PhalApi文档,phalapi在线文档,phalapi官方文档">

    <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" media="screen">
    <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.css" media="screen">
    <link rel="stylesheet" href="../css/main.css" />
    <link rel="stylesheet" href="../css/loading_bar.css" />
    <link rel="icon" href="http://webtools.qiniudn.com/dog_catch.png" type="image/x-icon" />
    <script src="../js/pace.min.js"></script>
	
	<!-- 代码高亮 -->
	<link rel="stylesheet" href="../css/highlight.min.css">
	<script src="../js/highlight.min.js"></script>
	<script>hljs.initHighlightingOnLoad();</script>
    
</head>

<body>
    <!-- navbar start -->
    <div class="navbar navbar-default navbar-fixed-top" id="mainnav" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand website_name" href="/"><!-- <span>PhalApi</span> --><strong>PhalApi</strong></a>
            </div>
            <div class="navbar-collapse collapse" aria-expanded="false" style="height: 1px;">
                <ul class="nav navbar-nav navbar-right" id="nav_bar">
                    <li><a href="/"><span class="glyphicon glyphicon-home"></span></a></li>
                    <li><a href="http://qa.phalapi.net/">社区</a></li>
                    <li><a href="/download.html">下载</a></li>
                    <li class="active"><a href="/wikis/">文档</a></li>
                    <li><a href="http://www.ituring.com.cn/book/2405">书籍</a></li>
                    <li><a href="http://demo.phalapi.net/">体验</a></li>
                    <li><a href="/about.html">关于</a></li>
                    <li><a href="/donate.html">贡献</a></li>
                    <li><a href="/wikis/en/">English</a></li>
                </ul>
            </div>
            <!--/.nav-collapse -->
        </div>
    </div>
    <!-- navbar end -->


    <div id="content">
        <div class="container">
            <div class="row row-md-flex row-md-flex-wrap">
                <h4><a href="/wikis/%5B2.12%5D-%E6%B5%B7%E9%87%8F%E6%95%B0%E6%8D%AE%EF%BC%9A%E5%8F%AF%E9%85%8D%E7%BD%AE%E7%9A%84%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8.html">上一章</a>   <a href="/wikis/">文档首页</a>   <a href="/wikis/%5B2.14%5D-%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91%EF%BC%9A%E6%84%8F%E5%9B%BE%E5%AF%BC%E5%90%91%E7%BC%96%E7%A8%8B%E4%B8%8B%E7%9A%84%E6%8E%A5%E5%8F%A3%E5%BC%80%E5%8F%91.html">下一章</a></h4>
<hr />
<p>本章主要讲解调试模式的相关操作，如何快速排查和定位开发过程中所遇到的问题和线上发生的故障。  </p>
<blockquote>
<p>温馨提示：以下内容基于PhalApi 1.4.0 版本及以上。  </p>
</blockquote>
<h2>2.13.1 开启调试调试</h2>
<p>开启调试模式很简单，主要有两种方式：  </p>
<ul>
<li><strong>单次请求开启调试</strong>：默认添加请求参数<code>&amp;__debug__=1</code>  </li>
<li><strong>全部请求开启调试</strong>：把配置文件<code>./Config/sys.php</code>文件中的配置改成<code>'debug' =&gt; true,</code>  </li>
</ul>
<p>请特别注意，在实际项目中，调试参数不应使用默认的调试参数，而应各自定义，使用更复杂的参数，从而减少暴露敏感或者调试信息的风险。例如：   </p>
<ul>
<li>不推荐的做法：<code>&amp;__debug__=1</code>  </li>
<li>一般的做法：<code>&amp;__phalapi_debug__=1</code>  </li>
<li>更好的做法：<code>&amp;__phalapi_debug__=202cb962ac59075b964b07152d234b70</code>  </li>
</ul>
<h2>2.13.2 调试信息有哪些？</h2>
<blockquote>
<p>温馨提示：调试信息仅有当在开启调试模式后，才会返回。  </p>
</blockquote>
<p>正常响应的情况下，当开启调试模式后，会返回多一个<code>debug</code>字段，里面有相关的调试信息。如下所示：  </p>
<pre><code>{
    "ret": 200,
    "data": {
    },
    "msg": "",
    "debug": {
        "stack": [  // 自定义埋点信息
        ],
        "sqls": [  // 全部执行的SQL语句
        ]
    }
}</code></pre>
<p>在发生异常时，最初的框架的处理方式是直接报500错误。现在调整为，当开启调试模式后，会将发生的异常转换为对应的结果按结果格式返回，即其结构会变成以下这样：  </p>
<pre><code>{
    "ret": 0,  // 异常时的错误码
    "data": [],
    "msg": "", // 异常时的错误信息
    "debug": {
        "exception": [  // 异常时的详细堆栈信息
        ],
        "stack": [  // 自定义埋点信息
        ],
        "sqls": [  // 全部执行的SQL语句
        ]
    }
}</code></pre>
<h3>(1) debug.sqls全部执行的SQL语句</h3>
<p>所执行的全部SQL语句，会由框架自动搜集并统计。最后显示的信息格式是：  </p>
<pre><code>[序号 - 当前SQL的执行时间ms]所执行的SQL语句及参数列表</code></pre>
<p>示例：  </p>
<pre><code>[1 - 0.32ms]SELECT * FROM tbl_user WHERE (id = ?); -- 1</code></pre>
<p>表示是第一条执行的SQL语句，消耗了0.32毫秒，SQL语句是<code>SELECT * FROM tbl_user WHERE (id = ?);</code>，其中参数是1。  </p>
<p>又如，假设我们编写一个这样获取用户总人数的示例接口。  </p>
<pre><code class="language-php">// $ vim ./Demo/Api/User.php
class Api_User extends PhalApi_Api {
    public function amount() {
        return DI()-&gt;notorm-&gt;user-&gt;count('id');
    }   
}</code></pre>
<p>然后通过<code>/?service=User.Amount&amp;__debug__=1</code>请求该接口，可以看到类似这样的返回结果。  </p>
<pre><code>{
    "ret": 200,
    "data": "49", // 共49个用户
    "msg": "",
    "debug": {
        "stack": [
            "[#0 - 0ms]/home/apps/projects/PhalApi/Public/index.php(6)"
        ],
        "sqls": [
            "[1 - 0.3ms]SELECT COUNT(id) FROM tbl_user;" // 本次所执行的SQL语句
        ]
    }
}</code></pre>
<blockquote>
<p>温馨提示：这只是一个示例。实际项目中，不推荐直接在Api直接操作数据库，也不推荐返回非数组格式的data。  </p>
</blockquote>
<h3>(2) debug.stack自定义埋点信息</h3>
<p>埋点信息的格式如下：  </p>
<pre><code>[#序号 - 距离最初节点的执行时间ms - 节点标识]代码文件路径(文件行号)</code></pre>
<p>示例：  </p>
<pre><code>[#0 - 0ms]/home/apps/projects/PhalApi/Public/index.php(6)</code></pre>
<p>表示，这是第一个埋点（由框架自行添加），执行时间为0毫秒，所在位置是文件<code>/home/apps/projects/PhalApi/Public/index.php</code>的第6行。即第一条的埋点发生在框架初始化时：  </p>
<pre><code class="language-php">// $ vim ./Public/init.php

if (DI()-&gt;debug) {
    // 启动追踪器
    DI()-&gt;tracer-&gt;mark();
}</code></pre>
<p>与SQL语句的调试信息不同的是，自定义埋点则需要开发人员根据需要自行纪录，可以使用全球追踪器<code>DI()-&gt;tracer</code>进行纪录，其使用如下：  </p>
<pre><code class="language-php">// 添加纪录埋点
DI()-&gt;tracer-&gt;mark();

// 添加纪录埋点，并指定节点标识
DI()-&gt;tracer-&gt;mark('DO_SOMETHING');</code></pre>
<p>通过上面方法，可以对执行经过的路径作标记。你可以指定节点标识，也可以不指定。对一些复杂的接口，可以在业务代码中添加这样的埋点，追踪接口的响应时间，以便进一步优化性能。当然，更专业的性能分析工具推荐使用XHprof。  </p>
<blockquote>
<p>参考资料：<a href="http://git.oschina.net/dogstar/PhalApi-Library/tree/master/Xhprof">XHprof扩展类库</a>  </p>
</blockquote>
<p>继续上面的示例，在进行数据库操作前后，我们添加相应的操作埋点。  </p>
<pre><code class="language-php">    public function amount() {
        DI()-&gt;tracer-&gt;mark('开始读取数据库');

        $rs = DI()-&gt;notorm-&gt;user-&gt;count('id');

        DI()-&gt;tracer-&gt;mark('读取完毕');

        return $rs;
    }  </code></pre>
<p>再次请求，会看到类似以下的返回结果。  </p>
<pre><code>{
    "ret": 200,
    "data": "49",
    "msg": "",
    "debug": {
        "stack": [
            "[#0 - 0ms]/home/apps/projects/PhalApi/Public/index.php(6)",
            "[#1 - 5.5ms - 开始读取数据库]/home/apps/projects/PhalApi/Demo/Api/User.php(74)",
            "[#2 - 6.4ms - 读取完毕]/home/apps/projects/PhalApi/Demo/Api/User.php(78)"
        ],
        "sqls": [
            "[1 - 0.3ms]SELECT COUNT(id) FROM tbl_user;"
        ]
    }
}</code></pre>
<p>可以看出，在“开始读取数据库”前消耗了5.5毫秒，以及相关的代码位置。  </p>
<h3>(3) debug.exception异常堆栈信息</h3>
<p>当有未能捕捉的接口异常时，开启调试模式后，框架会把对应的异常转换成对应的返回结果，而不是像最初那样直接500，页面空白。这些是由框架自动处理的。  </p>
<p>继续上面的示例，让我们故意制造一些麻烦，手动抛出一个异常。  </p>
<pre><code class="language-php">    public function amount() {
        ... ... 

        throw new Exception('这是一个演示异常调试的示例', 501);

        return $rs;
    }</code></pre>
<p>再次请求后，除了上面的SQL语句和自定义埋点信息外，还会看到这样的异常堆栈信息。  </p>
<pre><code>{
    "ret": 501,
    "data": [],
    "msg": "这是一个演示异常调试的示例",
    "debug": {
        "exception": [
            {
                "function": "amount",
                "class": "Api_User",
                "type": "-&amp;gt;",
                "args": []
            },
            ... ...
        ],
        "stack": [
            ... ...
        ],
        "sqls": [
            ... ...
        ]
    }
}</code></pre>
<p>然后便可根据返回的异常信息进行排查定位问题。  </p>
<h3>(4) 添加自定义调试信息</h3>
<p>当需要添加其他调试信息时，可以使用<code>DI()-&gt;response-&gt;setDebug()</code>进行添加。  </p>
<p>如： </p>
<pre><code class="language-php">$x = 'this is x';
$y = array('this is y');
DI()-&gt;response-&gt;setDebug('x', $x);
DI()-&gt;response-&gt;setDebug('y', $y);</code></pre>
<p>请求后，可以看到：  </p>
<pre><code>    "debug": {
        "x": "this is x",
        "y": [
            "this is y"
        ]
    }</code></pre>
<h2>2.13.3 一个错误的接口开发</h2>
<p>有时，在进行接口开发时，会需要进行批量获取的功能，如列表。但很多开发的同学可能会因为时间赶或者没有意识去对SQL查询进行优化，或者甚至不知道自己的接口背后隐藏着多少问题。下面是一个错误的开发示例。  </p>
<h3>(1)新增的批量获取接口</h3>
<p>假设我们在开发一个国际的项目，并且运行良好，BOSS说因业务需要，要加多一个接口以支持批量获取用户的基本信息，提供给国外某知名的社交平台调用。</p>
<p>于是乎，我们很快就根据原来的单个获取接口实现了新的接口：</p>
<pre><code class="language-php">//$vim ./Demo/Api/User.php
&lt;?php

class Api_User extends PhalApi_Api {

    public function getRules() {
        return array(
            //...
            'getMultiBaseInfo' =&gt; array(
                'user_ids' =&gt; array('name' =&gt; 'user_ids', 'type' =&gt; 'array', 'format' =&gt; 'explode', 'require' =&gt; true),
            ),
        );
    }

    //...

    public function getMultiBaseInfo() {
        $rs = array('code' =&gt; 0, 'msg' =&gt; '', 'list' =&gt; array());

        $domain = new Domain_User();
        foreach ($this-&gt;user_ids as $userId) {
            $rs['list'][] = $domain-&gt;getBaseInfo($userId);
        }

        return $rs;
    }
}
</code></pre>
<h3>(2)运行调用一下</h3>
<p>显然，我们可以很清楚地调用新增的接口：</p>
<pre><code>http://dev.phalapi.com/demo/?service=User.GetMultiBaseInfo&amp;user_ids=1,2,3</code></pre>
<p>可返回：</p>
<pre><code>{
    "ret": 200,
    "data": {
        "code": 0,
        "msg": "",
        "list": [
            {
                "id": "1",
                "name": "dogstar",
                "note": "oschina"
            },
            {
                "id": "2",
                "name": "Tom",
                "note": "USA"
            },
            {
                "id": "3",
                "name": "King",
                "note": "game"
            }
        ]
    },
    "msg": ""
}</code></pre>
<p>假设我们已经有了这样的数据库表数据：</p>
<pre><code class="language-sql">INSERT INTO `tbl_user` VALUES ('1', 'dogstar', 'oschina');
INSERT INTO `tbl_user` VALUES ('2', 'Tom', 'USA');
INSERT INTO `tbl_user` VALUES ('3', 'King', 'game');</code></pre>
<h3>(3)这样的问题？</h3>
<p>这样的问题，在对外黑盒调用的客户端同学是发现不了的，对于测试人员来说也是无法感知的。但所犯的错误也是显然易见的，就是没有进行SQL的批量查询优化，造成了很多不必要的重复查询。<br />
这里，根据前面学习的调试方式，则我们可以快速发现存在的问题：</p>
<pre><code>http://dev.phalapi.com/demo/?service=User.GetMultiBaseInfo&amp;user_ids=1,2,3&amp;__debug__=1</code></pre>
<p>如下返回，我们看到了很多重复类似的查询语句。</p>
<pre><code>{
    ... ...
    "debug": {
        .... ...
        "sqls": [
            "[1 - 0.34ms]SELECT * FROM tbl_user WHERE (id = ?); -- 1",
            "[2 - 0.16ms]SELECT * FROM tbl_user WHERE (id = ?); -- 2",
            "[3 - 0.16ms]SELECT * FROM tbl_user WHERE (id = ?); -- 3"
        ]
    }
}</code></pre>
<h3>(4)如何改进？</h3>
<p>这是一个很基本的问题，当然在实际项目中不会普通存在，这里只是作为一个示例加以说明。但让人失望的是，实际项目确实存在为数不少的这样的情况。可能是新人的技术和意识问题，也有可能是老同学的态度问题。所以，优化这么一个接口的批量SQL查询不难，难的是如何才能让新、老同学都注重这块的SQL查询优化呢？而不是等到线上服务器异常崩溃后再来推托责任。<br />
具体的代码改进，留给读者自己实践了。毕竟，看了，实践了，才会真正深刻地掌握。</p>
<h2>2.13.4 由此引申</h2>
<ul>
<li>这里不专门讲述SQL的优化，但也顺便提供一些SQL查询优化的建议：</li>
<li>使用批量查询，而不是N次循环查询！</li>
<li>重复的数据，不要重复获取；</li>
<li>根据需要，按需要获取表字段，而不是SELECT *；</li>
<li>针对频繁的搜索字段，建立必要的索引，以加快查询速度；</li>
<li>使用关联查询，而不是粗暴地类似：where uid IN (... 这里是成千上W个用户ID ...)；</li>
<li>针对单条SQL语句执行时间超过1秒的，重点优化；</li>
</ul>
<p>搞定，收工，开饭！</p>
<hr />
<h4><a href="/wikis/%5B2.12%5D-%E6%B5%B7%E9%87%8F%E6%95%B0%E6%8D%AE%EF%BC%9A%E5%8F%AF%E9%85%8D%E7%BD%AE%E7%9A%84%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8.html">上一章</a>   <a href="/wikis/">文档首页</a>   <a href="/wikis/%5B2.14%5D-%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91%EF%BC%9A%E6%84%8F%E5%9B%BE%E5%AF%BC%E5%90%91%E7%BC%96%E7%A8%8B%E4%B8%8B%E7%9A%84%E6%8E%A5%E5%8F%A3%E5%BC%80%E5%8F%91.html">下一章</a></h4><div style="float: left">
<h4>
<a href="http://qa.phalapi.net/">还有疑问？欢迎到社区提问！</a>
</h4>
</div>
            </div>
        </div>
    </div>

 <!-- 广告位 -->
<div class="grid-wrapper desktop-only">
	<p align="center">
		<a href="http://7xiz2f.com1.z0.glb.clouddn.com/%E6%88%91%E7%9A%84%E5%90%8D%E5%AD%97%E5%8F%AB%EF%BC%9A%CF%80%E6%A1%86%E6%9E%B6%20-%20PhalApi%202016%E5%B9%B4%E5%BA%A6%E5%BC%80%E6%BA%90%E6%80%BB%E7%BB%93%20-%20%E5%AE%98%E6%96%B9%E5%87%BA%E5%93%81.pdf" target="blank"><img width="950" height="100" src="http://7xiz2f.com1.z0.glb.clouddn.com/ad_20170104.png"></a>
		<a href="http://www.itran.cc/" target="blank"><img width="950" height="100" src="http://7xslqv.com1.z0.glb.clouddn.com/images/9/69/b88bc92455dc1239f9a5bf8d72929.png"></a>
		<a href="http://web-tools.phalapi.net/" target="blank"><img width="950" height="100" src="http://7xslqv.com1.z0.glb.clouddn.com/images/7/d0/88f523566c482296aecc43d185ca2.png"></a>
	</p>
</div>

        <!-- footer -->
        <div class="footer">
            <div class="link container ">
                <div class=" col-xs-7 col-sm-7 col-md-7 col-lg-7 ">
                    <h3>PhalApi (π框架)</h3>
                    <p>一个轻量级PHP开源接口框架，专注于接口服务开发，支持HTTP/SOAP/RPC协议，拥有自动生成的在线文档、多种开发语言的客户端SDK包以及可重用的扩展类库，可用于快速搭建微服务、RESTful接口或Web
                        Services。</p>
                    <!-- <p>:</p> -->
                    <span>友情链接:</span>
                    <a href="https://www.phalapi.net/" target="_blank"> PhalApi </a>
                    <a href="http://www.oschina.net/" target="_blank"> 开源中国 </a>
                    <a href="https://www.itran.cc/" target="_blank">艾翻译</a>
                    <a href="http://www.phalconphp.com/en/" target="_blank">Phalcon</a>
                    <a href="https://phpunit.de/manual/3.7/zh_cn/automating-tests.html" target="_blank">PHPUnit</a>
                    <a href="http://www.thoughtworks.com/cn/" target="_blank">ThoughtWorks</a>
                    <a href="mailto:chanzonghuang@gmail.com">友链交换</a>

                </div>
                <div class=" col-xs-5 col-sm-5 col-md-5 col-lg-5 contact_us ">
                    <h3>联系我们</h3>
                    <a href="https://github.com/phalapi/phalapi"><img src="../images/github.png" alt="github" /></a>
                    <a href="http://weibo.com/p/100808d236e99beb645bfb56ed1c37dde9b8bd?k=phalapi%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6&from=501&_from_=huati_topic"><img src="../images/weibo.png"  alt="weibo" /></a>
                    <a href="http://7xslqv.com1.z0.glb.clouddn.com/images/1/b0/894fde762850cb11250253f7d5ede.png"><img src="../images/zfb.png"  alt="zfb" /></a>
                    <a href="https://jq.qq.com/?_wv=1027&k=4A6reum"><img src="../images/qq.png"  alt="qq" /></a>
					<a href="http://git.oschina.net/dogstar/PhalApi"><img src="../images/git-oschina.png"  alt="git-oschina" /></a>
                </div>
                <div class="cls"></div>
                <div class="text-center copy_right"> ©2015-2017 PhalApi All Rights Reserved. <a href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备15028808号</a>

				<script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1255326144'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s4.cnzz.com/z_stat.php%3Fid%3D1255326144%26show%3Dpic' type='text/javascript'%3E%3C/script%3E"));</script>
                </div>
            </div>
        </div>
    </div>


    <a href="https://github.com/phalapi"><img id="gitHub_fllow" style="position: fixed; top: 0; right: 0; border: 0;z-index:9999" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a>


    <script src="../js/jquery-1.11.1.min.js"></script>
    <script src="../js/bootstrap.min.js"></script>
</body>

</html>